package org.light.limitedverb;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.light.core.Writeable;
import org.light.domain.Domain;
import org.light.domain.Field;
import org.light.domain.Method;
import org.light.domain.Signature;
import org.light.domain.Statement;
import org.light.domain.StatementList;
import org.light.domain.Type;
import org.light.exception.ValidateException;
import org.light.utils.DomainTokenUtil;
import org.light.utils.StringUtil;
import org.light.utils.TableStringUtil;
import org.light.utils.WriteableUtil;

public class CountSearchByFieldsRecords extends NoControllerVerb {
	protected static Logger logger = Logger.getLogger(CountSearchByFieldsRecords.class);
	protected Set<Field> deniedFields = new TreeSet<>();
	@Override
	public Method generateDaoImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("CountSearch" + domain.getCapFirstPlural() + "ByFieldsRecords");
			method.setNoContainer(false);
			method.addSignature(new Signature(1, "&self",""));
			method.addSignature(new Signature(2, this.domain.getSnakeDomainName()+"_query_request",this.domain.getCapFirstDomainName()+"QueryRequest"));
			method.setReturnType(new Type("Result<CountNum, Error>"));
			
			List<Writeable> sList = new ArrayList<Writeable>();
			long serial = 1000L;
			sList.add(new Statement(serial+1000L,2,"let mut query = r#\"select count(*) as countNum from "+ domain.getDbPrefix() + TableStringUtil.domainNametoTableName(domain)+" where 1=1 \"#.to_string();"));
			for (Field f: this.domain.getSearchFields()) {
				if (!deniedFields.contains(f)) {
					if ("String".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
						sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and "+f.getSnakeFieldName()+" like '%\","+f.getSnakeFieldName()+",\"%' \");"));
						sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
						sList.add(new Statement(serial+8000L,2,"}"));
					}else if ("i32".equals(f.getFieldType())||"i64".equals(f.getFieldType())||"f32".equals(f.getFieldType())||"f64".equals(f.getFieldType())){
						sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
						sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and "+f.getSnakeFieldName()+" = \","+f.getSnakeFieldName()+",\" \");"));
						sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
						sList.add(new Statement(serial+8000L,2,"}"));
					}else if ("bool".equals(f.getFieldType())){
						sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
						sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() && ("+f.getSnakeFieldName()+"==\"true\" || "+f.getSnakeFieldName()+" ==\"false\"){"));
						sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and "+f.getSnakeFieldName()+" = \","+f.getSnakeFieldName()+",\" \");"));
						sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
						sList.add(new Statement(serial+8000L,2,"}"));
					}else if ("datetime".equalsIgnoreCase(f.getFieldType())||"date".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
						sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and date(" + f.getSnakeFieldName() + ") = date('\","
								+ f.getSnakeFieldName() + ",\"') \"); "));
						sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
						sList.add(new Statement(serial+8000L,2,"}"));
					}					
					serial += 10000L;
				}
			}
			sList.add(new Statement(serial+39000L,2,"let result = sqlx::query_as(&query)"));
			sList.add(new Statement(serial+40000L,2,".fetch_one(&(&*self.pool).clone().unwrap())"));
			sList.add(new Statement(serial+41000L,2,".await;"));

			sList.add(new Statement(serial+43000L,2,"return result;"));
			if (this.domain.getDbType().equalsIgnoreCase("oracle")) {
				method.setMethodStatementList(getOracleDaoimplStatementList());
			}else {
				method.setMethodStatementList(WriteableUtil.merge(sList));
			}
			return method;
		}
	}
	
	public StatementList getOracleDaoimplStatementList() throws Exception {
		List<Writeable> sList = new ArrayList<Writeable>();
		long serial = 1000L;
		sList.add(new Statement(serial,1,"let conn = (&*self.pool).clone().unwrap().get().unwrap();"));
			
		sList.add(new Statement(serial+1000L,2,"let mut query = r#\"select count(*) as countNum from " + domain.getDbPrefix() + TableStringUtil.domainNametoTableName(domain)+" where 1=1 \"#.to_string();"));
		for (Field f: this.domain.getSearchFields()) {
			if (!deniedFields.contains(f)) {
				if ("String".equalsIgnoreCase(f.getFieldType())){
					sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
					sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
					sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and "+f.getSnakeFieldName()+" like '%\","+f.getSnakeFieldName()+",\"%' \");"));
					sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
					sList.add(new Statement(serial+8000L,2,"}"));
				}else if ("i32".equals(f.getFieldType())||"i64".equals(f.getFieldType())||"f32".equals(f.getFieldType())||"f64".equals(f.getFieldType())){
					sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
					sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
					sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and "+f.getSnakeFieldName()+" = \","+f.getSnakeFieldName()+",\" \");"));
					sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
					sList.add(new Statement(serial+8000L,2,"}"));
				} else if ("bool".equals(f.getFieldType())){
					sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
					sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() && ("+f.getSnakeFieldName()+"==\"true\" || "+f.getSnakeFieldName()+" ==\"false\"){"));
					sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and "+f.getSnakeFieldName()+" = \","+f.getSnakeFieldName()+",\" \");"));
					sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
					sList.add(new Statement(serial+8000L,2,"}"));
				} else if ("date".equalsIgnoreCase(f.getFieldType())){
					sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
					sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
					sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and to_char("+f.getSnakeFieldName()+",'yyyy-MM-dd') = '\","+f.getSnakeFieldName()+",\"' \");"));
					sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
					sList.add(new Statement(serial+8000L,2,"}"));
				} else if ("datetime".equalsIgnoreCase(f.getFieldType())){
					sList.add(new Statement(serial+4000L,2,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
					sList.add(new Statement(serial+5000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
					sList.add(new Statement(serial+6000L,3,"let query_"+f.getSnakeFieldName()+"  = format!(\"{}{}{}\",\" and to_char("+f.getSnakeFieldName()+",'yyyy-MM-dd') = to_char(to_date('\","+f.getSnakeFieldName()+",\"','yyyy-MM-dd HH24:mi:ss'),'yyyy-MM-dd') \");"));
					sList.add(new Statement(serial+7000L,3,"query = format!(\"{} {}\", query, query_"+f.getSnakeFieldName()+");"));
					sList.add(new Statement(serial+8000L,2,"}"));
				}
				serial += 10000L;
			}
		}

		sList.add(new Statement(serial+3000L,1,"let mut stmt = conn"));
		sList.add(new Statement(serial+4000L,1,".statement(&query)"));
		sList.add(new Statement(serial+5000L,1,".build()?;"));
		sList.add(new Statement(serial+6000L,1,"let result:i64 = stmt.query_row_as(&[]).unwrap();"));
		sList.add(new Statement(serial+7000L,1,"Ok(CountNum{count_num:result})"));

		return WriteableUtil.merge(sList);
	}

	@Override
	public String generateDaoImplMethodString() throws Exception {
		if (this.denied)
			return null;
		else {
			Method m = this.generateDaoImplMethod();
			String s = m.generateMethodString();
			return s;
		}
	}

	@Override
	public String generateDaoImplMethodStringWithSerial() throws Exception {
		if (this.denied)
			return null;
		else {
			Method m = this.generateDaoImplMethod();
			m.setContent(m.generateMethodContentStringWithSerial());
			m.setMethodStatementList(null);
			return m.generateMethodString();
		}
	}

	@Override
	public Method generateDaoMethodDefinition() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("CountSearch" + this.domain.getCapFirstPlural() + "ByFieldsRecords");
			method.setReturnType(new Type("int64"));
			method.addSignature(new Signature(1, "db","*sql.DB"));
			method.addSignature(new Signature(2, this.domain.getLowerFirstDomainName(), this.domain.getType(),
					this.domain.getPackageToken()));
			method.addSignature(new Signature(3, "ignoreMap","map[string]bool"));
			return method;
		}
	}

	@Override
	public String generateDaoMethodDefinitionString() throws Exception {
		if (this.denied)
			return null;
		else {
			return generateDaoMethodDefinition().generateMethodDefinition();
		}
	}

	@Override
	public Method generateServiceMethodDefinition() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("CountSearch" + this.domain.getCapFirstPlural() + "ByFieldsRecords");
			method.setReturnType(new Type("int64"));
			method.addSignature(new Signature(1, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.addSignature(new Signature(2, "ignoreMap","map[string]bool"));
			return method;
		}
	}

	@Override
	public String generateServiceMethodDefinitionString() throws Exception {
		if (this.denied)
			return null;
		else {
			return generateServiceMethodDefinition().generateMethodDefinition();
		}
	}

	@Override
	public Method generateServiceImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("CountSearch" + this.domain.getCapFirstPlural() + "ByFieldsRecords");
			method.addSignature(new Signature(1, this.domain.getSnakeDomainName()+"_query_request",this.domain.getCapFirstDomainName()+"QueryRequest"));
			method.setReturnType(new Type("Result<CountNum, Error>"));
			Method daomethod = this.generateDaoMethodDefinition();
			List<Writeable> sList = new ArrayList<Writeable>();
			sList.add(new Statement(1000L,1,"let app_state = init_db();"));
			sList.add(new Statement(2000L,1,"app_state.await.context."+StringUtil.getSnakeName(this.domain.getPlural())+"."+StringUtil.getSnakeName(this.getVerbName())+"("+this.domain.getSnakeDomainName()+"_query_request).await"));

			method.setMethodStatementList(WriteableUtil.merge(sList));

			return method;
		}
	}

	@Override
	public String generateServiceImplMethodString() throws Exception {
		if (this.denied)
			return null;
		else {
			return generateServiceImplMethod().generateMethodString();
		}
	}

	@Override
	public String generateServiceImplMethodStringWithSerial() throws Exception {
		if (this.denied)
			return null;
		else {
			Method m = this.generateServiceImplMethod();
			m.setContent(m.generateMethodContentStringWithSerial());
			m.setMethodStatementList(null);
			return m.generateMethodString();
		}
	}

	public CountSearchByFieldsRecords(Domain domain) throws ValidateException {
		super();
		this.domain = domain;
		this.dbType = "MariaDB";
		this.denied = domain.isVerbDenied("SearchByFieldsByPage");
		this.verbName = "countSearch" + this.domain.getCapFirstPlural() + "ByFieldsRecords";
	}
	
	public CountSearchByFieldsRecords(Domain domain,String dbType) throws ValidateException {
		super();
		this.domain = domain;
		this.dbType = dbType;
		this.denied = domain.isVerbDenied("SearchByFieldsByPage");
		this.verbName = "countSearch" + this.domain.getCapFirstPlural() + "ByFieldsRecords";
	}

	public CountSearchByFieldsRecords() {
		super();
		this.dbType = "MariaDB";
	}
	
	public CountSearchByFieldsRecords(String dbType) {
		super();
		this.dbType = dbType;
	}

	public Set<Field> getDeniedFields() {
		return deniedFields;
	}

	public void setDeniedFields(Set<Field> deniedFields) {
		this.deniedFields = deniedFields;
	}

	@Override
	public Method generateDummyDaoImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("CountSearch" + domain.getCapFirstPlural() + "ByFieldsRecords");
			method.setNoContainer(false);
			method.addSignature(new Signature(1, "&self",""));
			method.addSignature(new Signature(2, this.domain.getSnakeDomainName()+"_query_request",this.domain.getCapFirstDomainName()+"QueryRequest"));
			method.setReturnType(new Type("Result<CountNum, Error>"));
			
			List<Writeable> sList = new ArrayList<Writeable>();
			long serial = 1000L;
			for (Field f: this.domain.getSearchFields()) {
				if (!deniedFields.contains(f)) {
					sList.add(new Statement(serial,1,"let "+f.getSnakeFieldName()+" = "+this.domain.getSnakeDomainName()+"_query_request."+f.getSnakeFieldName()+".unwrap_or_default();"));
					serial += 1000L;
				}
			}
			sList.add(new Statement(serial+2000L,1,"let mut result:Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+"> = vec![];"));
			sList.add(new Statement(serial+3000L,1,"let _db = get_db();"));
			sList.add(new Statement(serial+4000L,1,"for "+this.domain.getSnakeDomainName()+" in _db {"));
			sList.add(new Statement(serial+5000L,2,"let mut matches = true;"));
			
			serial += 6000L;
			for (Field f: this.domain.getSearchFields()) {
				if (!deniedFields.contains(f)) {
					if ("String".equalsIgnoreCase(f.getFieldType())){					
						sList.add(new Statement(serial+1000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+2000L,3,"if !"+this.domain.getSnakeDomainName()+"."+f.getSnakeFieldName()+".contains(&"+f.getSnakeFieldName()+") {"));
						sList.add(new Statement(serial+3000L,4,"matches = false;"));
						sList.add(new Statement(serial+4000L,3,"}"));
						sList.add(new Statement(serial+5000L,2,"}"));
					}else if ("i32".equals(f.getFieldType())||"i64".equals(f.getFieldType())){
						sList.add(new Statement(serial+1000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+2000L,3,"if "+this.domain.getSnakeDomainName()+"."+f.getSnakeFieldName()+".to_string() != "+f.getSnakeFieldName()+" {"));
						sList.add(new Statement(serial+3000L,4,"matches = false;"));
						sList.add(new Statement(serial+4000L,3,"}"));
						sList.add(new Statement(serial+5000L,2,"}"));
					}else if ("f32".equals(f.getFieldType())||"f64".equals(f.getFieldType())){
						sList.add(new Statement(serial+1000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+2000L,3,"if "+this.domain.getSnakeDomainName()+"."+f.getSnakeFieldName()+" as f64 - "+f.getSnakeFieldName()+".parse::<f64>().unwrap() < 0.0001 as f64 {"));
						sList.add(new Statement(serial+3000L,4,"matches = false;"));
						sList.add(new Statement(serial+4000L,3,"}"));
						sList.add(new Statement(serial+5000L,2,"}"));
					}else if ("bool".equals(f.getFieldType())){
						sList.add(new Statement(serial+1000L,2,"if !"+f.getSnakeFieldName()+".is_empty() && ("+f.getSnakeFieldName()+"==\"true\" || "+f.getSnakeFieldName()+" ==\"false\") {"));
						sList.add(new Statement(serial+2000L,3,"if "+this.domain.getSnakeDomainName()+"."+f.getSnakeFieldName()+".to_string() != "+f.getSnakeFieldName()+" {"));
						sList.add(new Statement(serial+3000L,4,"matches = false;"));
						sList.add(new Statement(serial+4000L,3,"}"));
						sList.add(new Statement(serial+5000L,2,"}"));
					} else if ("datetime".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial+1000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+2000L,3,"if "+this.domain.getSnakeDomainName()+"."+f.getSnakeFieldName()+".unwrap_or_default().date().to_string() != NaiveDateTime::parse_from_str(&"+f.getSnakeFieldName()+",DATE_TIME_FORMAT).unwrap().date().to_string() {"));
						sList.add(new Statement(serial+3000L,4,"matches = false;"));
						sList.add(new Statement(serial+4000L,3,"}"));
						sList.add(new Statement(serial+5000L,2,"}"));
					} else if ("date".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial+1000L,2,"if !"+f.getSnakeFieldName()+".is_empty() {"));
						sList.add(new Statement(serial+2000L,3,"if "+this.domain.getSnakeDomainName()+"."+f.getSnakeFieldName()+".unwrap_or_default().to_string() != "+f.getSnakeFieldName()+" {"));
						sList.add(new Statement(serial+3000L,4,"matches = false;"));
						sList.add(new Statement(serial+4000L,3,"}"));
						sList.add(new Statement(serial+5000L,2,"}"));
					}				
					serial += 6000L;
				}
			}
			sList.add(new Statement(serial,2,"if matches {"));
			sList.add(new Statement(serial+1000L,3,"result.push("+this.domain.getSnakeDomainName()+");"));
			sList.add(new Statement(serial+2000L,2,"}"));
			sList.add(new Statement(serial+3000L,1,"}"));
			sList.add(new Statement(serial+4000L,1,"Ok(CountNum{count_num:result.len() as i64})"));
			
			method.setMethodStatementList(WriteableUtil.merge(sList));
			return method;
		}
	}

}
